package peer;

/**Represents the field of bits which are found at the end of status_response messages. This can be used when a message is sent,
 * and the field of bits has to be constructed; and when the message is received the and bit field has to be understood.
 * To use this to create a bit field to send:
 * - create a bit field of size equal to the number of chunks in the file
 * - tell this which chunks you have
 * - tell the created packet this is the bit field to use
 * To use this to interpret a bit field that has been received:
 * - create a bit field with the string that was received
 * - call get have chunk for each chunk
 * @author David Johnstone
 */
public class BitField
{
	boolean[] data;
	
	/**To create a new bit field which will then be filled up.
	 * @param size the number of chunks that this bit field will represent
	 */
	public BitField(int size)
	{
		if(size > 0)
		{
			data = new boolean[size];
			for(int i = 0; i < data.length; i++)
			{
				data[i] = false;
			}
		}
		else throw new IllegalArgumentException("Must be greater than 0!");
	}
	
	/**To create a new bit field from a string that another bit field has created 
	 * @param rawData the received string
	 */
	public BitField(String rawData)
	{
		//each character represents 8 bits
		data = new boolean[rawData.length() * 8];
		for(int i = 0; i < rawData.length(); i++)
		{
			//check each of the 8 bits in the character to see if the bit is set
			int val = rawData.charAt(i);
			//bitwise and the character and a mask (mask here is 0b10000000) to see if this bit is set
			if((val & 0x80) == 0x80)
			{
				data[i * 8] = true;
			}
			else
			{
				data[i * 8] = false;
			}
			if((val & 0x40) == 0x40)
			{
				data[i * 8 + 1] = true;
			}
			else
			{
				data[i * 8 + 1] = false;
			}
			if((val & 0x20) == 0x20)
			{
				data[i * 8 + 2] = true;
			}
			else
			{
				data[i * 8 + 2] = false;
			}
			if((val & 0x10) == 0x10)
			{
				data[i * 8 + 3] = true;
			}
			else
			{
				data[i * 8 + 3] = false;
			}
			if((val & 0x08) == 0x08)
			{
				data[i * 8 + 4] = true;
			}
			else
			{
				data[i * 8 + 4] = false;
			}
			if((val & 0x04) == 0x04)
			{
				data[i * 8 + 5] = true;
			}
			else
			{
				data[i * 8 + 5] = false;
			}
			if((val & 0x02) == 0x02)
			{
				data[i * 8 + 6] = true;
			}
			else
			{
				data[i * 8 + 6] = false;
			}
			if((val & 0x01) == 0x01)
			{
				data[i * 8 + 7] = true;
			}
			else
			{
				data[i * 8 + 7] = false;
			}
		}
	}
	
	public void setHaveChunk(int chunkIndex)
	{
		data[chunkIndex] = true;
	}
	
	public boolean getHaveChunk(int chunkIndex)
	{
		return data[chunkIndex];
	}
	
	public boolean hasCompleteFile()
	{
		for(boolean b: data)
		{
			if(false == b)
				return false;
		}
		
		return true;
	}
	
	public String toString()
	{
		StringBuilder sb = new StringBuilder();
		//we create one character for each eight bits
		for(int i = 0; i < data.length; i += 8)
		{
			byte val = 0x00;
			//set the first (left most) bit of the current byte if the bit is set 
			if(data[i])
			{
				val = (byte) (val | 0x80);
			}
			if(data[i + 1])
			{
				val = (byte) (val | 0x40);
			}
			if(data[i + 2])
			{
				val = (byte) (val | 0x20);
			}
			if(data[i + 3])
			{
				val = (byte) (val | 0x10);
			}
			if(data[i + 4])
			{
				val = (byte) (val | 0x08);
			}
			if(data[i + 5])
			{
				val = (byte) (val | 0x04);
			}
			if(data[i + 6])
			{
				val = (byte) (val | 0x02);
			}
			if(data[i + 7])
			{
				val = (byte) (val | 0x01);
			}
			sb.append((char) val);
		}
		return sb.toString();
	}
}
